// Copyright (C) 2002-2008 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "stdafx.h"
#include "CFpsParticleSystemSceneNode.h"
#include "CFpsParticleSizeAffector.h"
#include "CFpsParticleEmitter.h"
#include "Texture.h"
#include "game.h"

EmitterDesc** CFpsParticleSystemSceneNode::m_poolEmitterDesc = NULL;
bool* CFpsParticleSystemSceneNode::m_poolEmitterUsed = NULL;
int CFpsParticleSystemSceneNode::m_poolMaxParticles = PARTICLES_ON_EMITTER_OGLES2;
int CFpsParticleSystemSceneNode::m_poolMaxEmitters = MAX_EMITTERS_OGLES2;


CFpsParticleSystemSceneNode::CFpsParticleSystemSceneNode()
{
	Emitter = NULL;
	m_isEmittingEnabled = true;
	//m_emitterDesc = new EmitterDesc();
	//LastEmitTime = 0;

	//if( m_emitterDesc )
	//{
	//	//m_emitterDesc->Buffer->Material = Material;
	//	

	//	m_emitterDesc->Particles = GL_NEW SFpsParticle[maxParticles];
	//	m_emitterDesc->Buffer = GL_NEW tIPLGeometry();
	//	m_emitterDesc->Buffer->mGeometryMode = GL_QUADS;
	//	m_emitterDesc->Buffer->mCount = maxParticles * 4;
	//	m_emitterDesc->Buffer->mpVertexPositions = GL_NEW GLfloat[maxParticles * 4 * 3];
	//	m_emitterDesc->Buffer->mpTextureCoords = GL_NEW GLfloat[maxParticles * 4 * 2];
	//	m_emitterDesc->Buffer->mpVertexNormals = GL_NEW GLfloat[maxParticles * 4 * 3];
	//	m_emitterDesc->Buffer->mpVertexColors = GL_NEW GLfloat[maxParticles * 4 * 3];

	//	for( int i = 0; i < maxParticles; i+=8 )
	//	{
	//		m_emitterDesc->Buffer->mpTextureCoords[i] = 0;
	//		m_emitterDesc->Buffer->mpTextureCoords[i+1] = 0;

	//		m_emitterDesc->Buffer->mpTextureCoords[i+2] = 0;
	//		m_emitterDesc->Buffer->mpTextureCoords[i+3] = 1;

	//		m_emitterDesc->Buffer->mpTextureCoords[i+4] = 1;
	//		m_emitterDesc->Buffer->mpTextureCoords[i+5] = 1;

	//		m_emitterDesc->Buffer->mpTextureCoords[i+6] = 1;
	//		m_emitterDesc->Buffer->mpTextureCoords[i+7] = 0;			
	//		
	//	}

	//	m_iTex = LoadTexture("point");


	//	m_emitterDesc->used = 0;
	//	m_emitterDesc->parent = this;

	//	//m_emitterDesc->Particles[0] = GL_NEW SFpsParticle();

	//	
	//}
}

void CFpsParticleSystemSceneNode::SetPosition(float x, float y,bool bIsNew)
{
	m_posX = x;
	m_posY = y;
	if(bIsNew)
	{
		m_lastEmitPosX = m_posX;
		m_lastEmitPosY = m_posY;
	}
}

bool CFpsParticleSystemSceneNode::IsEnded()
{
	if( !m_emitterDesc )
	{
		return true;
	}

	/*if( m_isManualEmitControl && m_emitterDesc->used == 0 )
	{
		return true;
	}*/

	if( !m_isEmittingEnabled && m_emitterDesc->used == 0 )
	{
		return true;
	}

	//if( m_emitterDesc->used == 0 && (TotalEmitTime > RandomSysLifeTime || !m_isEmittingEnabled))
	//{
	//	//-1 stands for no restart
	//	if (RestartTimeMin == -1 || RestartTimeMax == -1)
	//	{
	//		return true;
	//	}
	//}

	return false;
}

bool CFpsParticleSystemSceneNode::IsActive()
{
	return (m_emitterDesc?true:false);
}

void CFpsParticleSystemSceneNode::Restart()
{
	Emitter->Reset();

	//m_bPaused = false;
	m_isEmittingEnabled = true;

	LastEmitTime = 0;
	m_nbEmitWatting = NB_EMIT_EACH_STEP;
	//SetRandomLifeTime();
}

void CFpsParticleSystemSceneNode::StopEmitting()
{
	m_isEmittingEnabled = false;
}

void CFpsParticleSystemSceneNode::Link()
{
	m_emitterDesc = LinkEmitterDescPointer();

	if( m_emitterDesc )
	{
		//m_emitterDesc->Buffer->Material = Material;

		m_emitterDesc->used = 0;
		m_emitterDesc->parent = this;
	}
}

void CFpsParticleSystemSceneNode::FreeLink()
{
	if( m_emitterDesc )
	{
		FreeLinkEmitterDescPointer( m_emitterDesc );
		m_emitterDesc = NULL;
	}
}

void CFpsParticleSystemSceneNode::render()
{

	vector3df horizontal;
	vector3df vertical;
	vector3df view;

	for( u32 i = 0; i < m_emitterDesc->used; ++i )
	{
		const SFpsParticle& particle = m_emitterDesc->Particles[i];

		float posX = particle.pos.X;
		float posY = particle.pos.Y;
		float hor = 0.5 * particle.width;
		float ver = 0.5f * particle.height;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 0] = posX-hor;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 1] = posY+ver;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 2] = 0;

		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 3] = posX-hor;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 4] = posY-ver;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 5] = 0;

		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 6] = posX+hor;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 7] = posY-ver;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 8] = 0;

		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 9] = posX+hor;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 10] = posY+ver;
		m_emitterDesc->Buffer->mpVertexPositions[i*12 + 11] = 0;
		
		//glColor4f (71.0/255, 146.0/255, 246.0/255, 1.0f);
	}

	const TexInfo * tex = getTexMgr()->GetTex(m_iTex);
	//setStateVector( tex->ShaderInfoId );
	
	// RUIZD: Changed GL_TEXTURE_RECTANGLE_ARB for GL_TEXTURE_2D
	glBindTexture( GL_TEXTURE_2D, tex->name ); 
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glColor4f (m_color.R, m_color.G, m_color.B, 1.0f);
	glScalef(1.0f, 1.0f, 1.0f);
	glTranslatef(m_posX, m_posY, 0.0f);
	

	tIPLGeometry _geom;

	_geom.mGeometryMode     = m_emitterDesc->Buffer->mGeometryMode;
	_geom.mCount            = m_emitterDesc->used * 4;
	_geom.mpVertexPositions = m_emitterDesc->Buffer->mpVertexPositions;
	_geom.mpVertexColors    = NULL;
	_geom.mpTextureCoords   = m_emitterDesc->Buffer->mpTextureCoords;	
	
	IPLGeometry_DrawDirectLinearAdd(&_geom);
	glLoadIdentity();
}

void CFpsParticleSystemSceneNode::doParticleSystem(float time)
{

	if( LastEmitTime == 0 )
	{
		LastEmitTime = time;
	}

	time = LastEmitTime + 0.03f;
	float now = time;
	float timediff = time - LastEmitTime;	
	LastEmitTime = time;


	float step = LastEmitTime* 0.5f;
	for( u32 i = 0; i < m_emitterDesc->used; ++i )
	{		
		m_color.R = 0.6f + 0.4f * sin(step);
		m_color.G = 0.6f + 0.4f * sin(step + PI * 2.0f / 3.0f);
		m_color.B = 0.6f + 0.4f * sin(step + PI * 4.0f / 3.0f);
	}


	bool bDelay = false;
	bool bEmit = true;



	if(bEmit && !m_isEmittingEnabled)
	{
		bEmit = false;
	}

	// run emitter

	if (Emitter && bEmit)
	{
		SFpsParticle* outArray = NULL;
		float time = timediff;
		/*if (time == 0 && StartDelay < 0) 
		{
			time = -StartDelay;
		}*/

		s32 newParticles = 0;

		newParticles = Emitter->emitt(now, time, outArray);
		//newParticles = Emitter->emitt2(now, m_nbEmitWatting, outArray);
		
		if (newParticles && outArray)
		{
			int used = m_emitterDesc->used;
			if( newParticles > m_poolMaxParticles - used )
			{
				newParticles = m_poolMaxParticles - used;
			}

			m_emitterDesc->used = used + newParticles;
			for(s32 i = used; i < m_emitterDesc->used; ++i )
			{
				m_emitterDesc->Particles[i] = outArray[i - used];
				m_emitterDesc->Particles[i].startTime += i*timediff/newParticles;
				//m_emitterDesc->Particles[i].pos.X += m_posX;
				//m_emitterDesc->Particles[i].pos.Y += m_posY;
				//AbsoluteTransformation.rotateVect(m_emitterDesc->Particles[i].startVector);				
			}
		}
		
		/*if(m_lastEmitPosX != m_posX && m_lastEmitPosY != m_posY)
		{
			m_nbEmitWatting = 10;
			newParticles = Emitter->emitt2(now, m_nbEmitWatting, outArray);

			if (newParticles && outArray)
			{
				int used = m_emitterDesc->used;
				if( newParticles > m_poolMaxParticles - used )
				{
					newParticles = m_poolMaxParticles - used;
				}

				m_emitterDesc->used = used + newParticles;
				for(s32 i = used; i < m_emitterDesc->used; ++i )
				{					
					m_emitterDesc->Particles[i] = outArray[i - used];
					m_emitterDesc->Particles[i].startTime += (m_emitterDesc->used - i - 1)*(m_emitterDesc->Particles[i].endTime - m_emitterDesc->Particles[i].startTime)/newParticles;
					m_emitterDesc->Particles[i].pos.X -= (m_posX-m_lastEmitPosX)*(i - used)/newParticles;
					m_emitterDesc->Particles[i].pos.Y -= (m_posY-m_lastEmitPosY)*(i - used)/newParticles;									
				}
			}			
			m_nbEmitWatting = 0;
		}*/

		m_lastEmitPosX = m_posX;
		m_lastEmitPosY = m_posY;
	}


	for(int i=0;i<AffectorList.size();i++)
	{
		(AffectorList[i])->affect(now - timediff, now, m_emitterDesc->Particles, m_emitterDesc->used );
	}

	// animate all particles
	float scale = timediff * 1000.0f;

	for( u32 i = 0; i < m_emitterDesc->used; )
	{
		if( now > m_emitterDesc->Particles[i].endTime )
		{
			for( u32 j = i + 1; j < m_emitterDesc->used; ++j )
			{
				m_emitterDesc->Particles[j - 1] = m_emitterDesc->Particles[j];
			}
			m_emitterDesc->used--;

			if(IsActive())
				m_nbEmitWatting++;
		}
		else
		{
			m_emitterDesc->Particles[i].pos += (m_emitterDesc->Particles[i].vector * scale);			
			++i;
		}
	}
}

void CFpsParticleSystemSceneNode::deserializeAttributes(IAttributes* in)
{
	//IFpsParticleSystemSceneNode::deserializeAttributes(in, options);

	u32 idx = 0;

	stringc texName = in->getAttributeAsString("Texture");
	m_iTex = LoadTexture(texName.c_str());

	Emitter = GL_NEW CFpsParticleEmitter();
	if (Emitter)
		idx = Emitter->deserializeAttributes(idx, in);

	++idx;

	AffectorList.clear();

	u32 cnt = in->getAttributeCount();

	while(idx < cnt)
	{
		const char* name = in->getAttributeName(idx);

		if (!name || strcmp("Affector", name))
			return;

		E_FPS_PARTICLE_AFFECTOR_TYPE atype =
			(E_FPS_PARTICLE_AFFECTOR_TYPE)in->getAttributeAsEnumeration(idx, FpsParticleAffectorTypeNames);

		IFpsParticleAffector* aff = 0;

		switch(atype)
		{
		/*case FPS_EPAT_ATTRACT:
			aff = createAttractionAffector();
			break;
		case FPS_EPAT_FADE_OUT:
			aff = createFadeOutParticleAffector();
			break;
		case FPS_EPAT_GRAVITY:
			aff = createGravityAffector();
			break;*/
		case FPS_EPAT_SIZE:
			aff = GL_NEW CFpsParticleSizeAffector();
			break;
		/*case FPS_EPAT_ROTATE:
			aff = createRotationAffector();
			break;
		case FPS_EPAT_SPIN:
			aff = createSpinAffector();
			break;
		case FPS_EPAT_NONE:*/
		default:
			break;
		}

		++idx;

		if (aff)
		{
			idx = aff->deserializeAttributes(idx, in);
			AffectorList.push_back(aff);			
		}
	}
	
}

IFpsParticleSystemSceneNode* CFpsParticleSystemSceneNode::clone()
{
	CFpsParticleSystemSceneNode* nb = GL_NEW CFpsParticleSystemSceneNode();

	CFpsParticleEmitter* emitter = (CFpsParticleEmitter*) Emitter;
	
	IFpsParticleEmitter* e = GL_NEW CFpsParticleEmitter(
			emitter->getBox(),
			emitter->getDirection(), emitter->getSpeedVariation(), 
			emitter->getMinParticlesPerSecond(), emitter->getMaxParticlesPerSecond(),
			emitter->getParticleWidth(), emitter->getParticleHeight(), emitter->getSizeVariation(),
			emitter->getMinStartColor(), emitter->getMaxStartColor(),
			emitter->getMinLifeTime(), emitter->getMaxLifeTime(),
			emitter->getMaxAngleDegrees(),
			emitter->getInitialRotMin(), emitter->getInitialRotMax());
	nb->setEmitter(e);

	for(int i=0;i<AffectorList.size();i++)
	{
		IFpsParticleAffector* aff = 0;

		switch ((AffectorList[i])->getType())
		{
			case FPS_EPAT_ATTRACT:
				{
				}
				break;
			case FPS_EPAT_FADE_OUT:
				{
				}
				break;
			case FPS_EPAT_GRAVITY:
				{
				}
				break;
			case FPS_EPAT_SIZE:
				{
					CFpsParticleSizeAffector* pAff = (CFpsParticleSizeAffector*)(AffectorList[i]);
					aff = GL_NEW CFpsParticleSizeAffector(pAff->getTargetWidth(), pAff->getTargetHeight(), pAff->getVariation(), pAff->getStartTimePercent(),
						pAff->getEndTimePercent());
				}
				break;
			case FPS_EPAT_ROTATE:
				{
				}
				break;
			case FPS_EPAT_SPIN:
				{
				}
				break;
		}

		if (aff) 
		{
			nb->addAffector(aff);
		}
	}

	nb->m_iTex = this->m_iTex;
	nb->LastEmitTime = 0;
	nb->m_color = this->m_color;
	return nb;
}

void CFpsParticleSystemSceneNode::setEmitter(IFpsParticleEmitter* emitter)
{
	SAFE_DELETE(Emitter);

	Emitter = emitter;
}

void CFpsParticleSystemSceneNode::addAffector(IFpsParticleAffector* affector)
{
	AffectorList.push_back(affector);
}


void CFpsParticleSystemSceneNode::InitPool( bool isOgles2 )
{
	CFpsParticleEmitter::reallocate( PARTICLES_ON_EMITTER_OGLES2 );

	m_poolMaxEmitters = MAX_EMITTERS_OGLES2;
	m_poolEmitterDesc = GL_NEW EmitterDesc*[m_poolMaxEmitters];
	m_poolEmitterUsed = GL_NEW bool[m_poolMaxEmitters];

	for( int i = 0; i < m_poolMaxEmitters; i++ )
	{

		m_poolMaxParticles = PARTICLES_ON_EMITTER_OGLES2;

		m_poolEmitterDesc[i] = GL_NEW EmitterDesc();

		m_poolEmitterDesc[i]->Particles = GL_NEW SFpsParticle[m_poolMaxParticles];
		//m_poolEmitterDesc[i]->Buffer = NEW SMeshBuffer();
		m_poolEmitterDesc[i]->Buffer = GL_NEW tIPLGeometry();
		m_poolEmitterDesc[i]->Buffer->mGeometryMode = GL_QUADS;
		m_poolEmitterDesc[i]->Buffer->mCount = m_poolMaxParticles * 4;
		m_poolEmitterDesc[i]->Buffer->mpVertexPositions = GL_NEW GLfloat[m_poolMaxParticles * 4 * 3];
		m_poolEmitterDesc[i]->Buffer->mpTextureCoords = GL_NEW GLfloat[m_poolMaxParticles * 4 * 2];
		m_poolEmitterDesc[i]->Buffer->mpVertexNormals = GL_NEW GLfloat[m_poolMaxParticles * 4 * 3];
		m_poolEmitterDesc[i]->Buffer->mpVertexColors = GL_NEW GLfloat[m_poolMaxParticles * 4 * 3];

		for( int j = 0; j < m_poolMaxParticles; j+=8 )
		{
			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j] = 0;
			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j+1] = 0;

			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j+2] = 0;
			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j+3] = 1;

			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j+4] = 1;
			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j+5] = 1;

			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j+6] = 1;
			m_poolEmitterDesc[i]->Buffer->mpTextureCoords[j+7] = 0;			
			
		}

		m_poolEmitterUsed[i] = false;
	}
}

void CFpsParticleSystemSceneNode::ResetPool( )
{
	for( int i = 0; i < m_poolMaxEmitters; i++ )
	{
		m_poolEmitterUsed[i] = false;
	}
}

void CFpsParticleSystemSceneNode::DestroyPool()
{
	if( m_poolEmitterDesc )
	{
		for( int i = 0; i < m_poolMaxEmitters; i++ )
		{
			if( m_poolEmitterDesc[i] )
			{
				SAFE_DEL_ARRAY( m_poolEmitterDesc[i]->Particles );

				SAFE_DEL_ARRAY(m_poolEmitterDesc[i]->Buffer->mpVertexPositions);
				SAFE_DEL_ARRAY(m_poolEmitterDesc[i]->Buffer->mpTextureCoords);
				SAFE_DEL_ARRAY(m_poolEmitterDesc[i]->Buffer->mpVertexNormals);
				SAFE_DEL_ARRAY(m_poolEmitterDesc[i]->Buffer->mpVertexColors);
				
				SAFE_DEL(m_poolEmitterDesc[i]->Buffer);

				SAFE_DEL( m_poolEmitterDesc[i] );
			}
		}
	}

	SAFE_DEL_ARRAY( m_poolEmitterDesc );
	SAFE_DEL_ARRAY( m_poolEmitterUsed );

	CFpsParticleEmitter::free();
}

EmitterDesc* CFpsParticleSystemSceneNode::LinkEmitterDescPointer()
{
	for( int i = 0; i < m_poolMaxEmitters; i++ )
	{
		if( !m_poolEmitterUsed[i] )
		{
			m_poolEmitterUsed[i] = true;
			return m_poolEmitterDesc[i];
		}
	}

	return NULL;
}

void CFpsParticleSystemSceneNode::FreeLinkEmitterDescPointer( EmitterDesc* ed )
{
	for( int i = 0; i < m_poolMaxEmitters; i++ )
	{
		if( m_poolEmitterDesc[i] == ed )
		{
			m_poolEmitterUsed[i] = false;
		}
	}
}
